﻿using Devil;
using UnityEngine;

namespace GameToolkit
{
    public partial class FStateMachineMono : MonoBehaviour, ICullingUpdate, ICullingComponent
    {
        protected FStateMachine m_StateMachine;
        [SerializeField]
        protected bool m_UseRealTime;
        [SerializeField]
        [Range(0,100)]
        protected int m_StackSize = 10;

        [HideInInspector]
        [SerializeField]
        protected FiniteState[] m_States;

        [HideInInspector]
        [SerializeField]
        protected FiniteStateTransition[] m_Transitions;

        [SerializeField]
        protected Object m_OtherImplement;

        [SerializeField]
        protected bool m_IsSubStateMachine;

        [SerializeField]
        private ECullDistance m_UpdateDistanceLv;
        [SerializeField]
        private ECullDistance m_AlwaysUpdateLv;

        public ECullingUpdateMode UpdateMode { get { return ECullingUpdateMode.Background | ECullingUpdateMode.Update; } }
        public int CulledUpdateDistanceLv { get { return (int)m_UpdateDistanceLv; } }
        public int AlwaysUpdateDistanceLv { get { return (int)m_AlwaysUpdateLv; } }
        public bool IsSubStateMachine { get { return m_IsSubStateMachine; } set { m_IsSubStateMachine = value; } }
        public float LastDeltaTime { get; private set; }
        bool mCullEnabled;
        ParallelDispatcher.Handler mLooper;

        protected void InitFSM()
        {
            if (m_StateMachine == null)
            {
                m_StateMachine = new FStateMachine(name, m_States.Length, m_Transitions.Length);
                m_StateMachine.MaxStackSize = m_StackSize;
                Object impl = m_OtherImplement == null ? this : m_OtherImplement;
                for (int i = 0; i < m_States.Length; i++)
                {
                    FiniteState state = m_States[i];
                    if (state.Init(transform, impl))
                    {
                        m_StateMachine.AddState(state, state.m_IsDefaultState);
                    }
                }
                for (int i = 0; i < m_Transitions.Length; i++)
                {
                    FiniteStateTransition trans = m_Transitions[i];
                    if (trans.Init(impl))
                    {
                        m_StateMachine.AddTransition(trans.m_FromState, trans.m_ToState, trans);
                    }
                }
            }
        }

        protected virtual void Awake()
        {
            //GenerateFSM();
            InitFSM();
        }

        protected virtual void Start()
        {
            if (!m_IsSubStateMachine)
                m_StateMachine.OnBegin();
        }

        protected virtual void OnEnable()
        {
            if (!mCullEnabled)
            //StartCoroutine("UpdateCor");
            {
                //if (mLooper == null)
                //{
                //    mLooper = FSMLooper;
                //}
                //MainThread.StartLooper(mLooper);
                mLooper.Abort();
                mLooper = ParallelUtils.ScheduleLooper(ExecuteUpdate, true);
            }
        }

        //bool FSMLooper(float deltaTime)
        //{
        //    ExecuteUpdate(m_UseRealTime ? Time.unscaledTime : deltaTime);
        //    return !mCullEnabled;
        //}

        protected virtual void OnDisable()
        {
            mLooper.Abort();
            //if (mLooper != null && !mCullEnabled)
            //    MainThread.StopLooper(mLooper);
        }

        protected virtual void OnDestroy()
        {
            if (m_StateMachine != null)
            {
                m_StateMachine.Release();
                m_StateMachine = null;
            }
        }

        public virtual void OnCullingEnabled(CullingCamera cam, ICullingBounds bounds)
        {
            mCullEnabled = true;
            mLooper.Abort();
            //if (mLooper != null)
            //    MainThread.StopLooper(mLooper);
            //StopCoroutine("UpdateCor");
        }

        public virtual void OnCullingDisabled() 
        {
            mCullEnabled = false;
        }

        public virtual void OnCullingUpdate(CullingCamera culler, ECullingUpdateMode mode)
        {
            if (isActiveAndEnabled)
                ExecuteUpdate(m_UseRealTime ? Time.unscaledDeltaTime : Time.deltaTime);
        }

        protected virtual void ExecuteUpdate(float deltaTime)
        {
            LastDeltaTime = deltaTime;
            m_StateMachine.OnTick(LastDeltaTime);
        }

        //IEnumerator UpdateCor()
        //{
        //    yield return null;
        //    while (!mCullEnabled)
        //    {
        //        ExecuteUpdate(m_UseRealTime ? Time.unscaledDeltaTime : Time.deltaTime);
        //        yield return null;
        //    }
        //}

        public FStateMachine FSM
        {
            get
            {
#if UNITY_EDITOR
                if (!Application.isPlaying)
                    return m_StateMachine;
#endif
                InitFSM();
                return m_StateMachine;
            }
        }

        public int StateLength { get { return m_States == null ? 0 : m_States.Length; } }

        public int TransitionLength { get { return m_Transitions == null ? 0 : m_Transitions.Length; } }

        public string CurrentStateName { get { return m_StateMachine == null ? null : m_StateMachine.CurrentStateName; } }

        public float CurrentStateTime { get { return m_StateMachine == null ? 0 : m_StateMachine.CurrentStateTime; } }

        public float TotalRuntime { get { return m_StateMachine == null ? 0 : m_StateMachine.TotalRuntime; } }

        public int CurrentStateTicks { get { return m_StateMachine == null ? 0 : m_StateMachine.CurrentStateTicks; } }

        public int TotalTicks { get { return m_StateMachine == null ? 0 : m_StateMachine.TotalTicks; } }

        public object Implement
        {
            get { return m_OtherImplement == null ? this : m_OtherImplement; }
        }

    }
}